#include "Mcp3903.h"

/*! \brief Creates a new instance of a MCP3903 Analog To Digital Converter
 * (ADC) and initializes the device to default values.
 *
 * Creates a new instance of a MCP3903 Analog To Digital Converter
 * (ADC)
 * @param spi
 * @param referenceVoltage
 * @param bitResolution
 */
Mcp3903::Mcp3903(Spi *spi, float referenceVoltage, unsigned char bitResolution)
{
	UINT32_VAL command;

	this->_spi = spi;
	this->_referenceVoltage = referenceVoltage;
	this->_quantizationLevels = (1 << bitResolution);

	// PHASE REGISTER
	// Phase = 0
	command.Val = 0;
	command.v[3] = 0b01001110;
	command.v[2] = 0b00000000;
	command.v[1] = 0b00000000;
	command.v[0] = 0b00000000;

	_spi->ChipSelect(true);
	_spi->Write(command.v[3]);
	_spi->Write(command.v[2]);
	_spi->Write(command.v[1]);
	_spi->Write(command.v[0]);
	_spi->ChipSelect(false);

	// GAIN REGISTER
	// Gain = 1, Boost off; 00 00 00
	command.Val = 0;
	command.v[3] = 0b01010000;
	command.v[2] = 0b00000000;
	command.v[1] = 0b00000000;
	command.v[0] = 0b00000000;

	_spi->ChipSelect(true);
	_spi->Write(command.v[3]);
	_spi->Write(command.v[2]);
	_spi->Write(command.v[1]);
	_spi->Write(command.v[0]);
	_spi->ChipSelect(false);

	// STATUS/COM REGISTER
	command.Val = 0;
	command.v[3] = 0b01010010;
	command.v[2] = 0b00011111;
	command.v[1] = 0b11010000;
	command.v[0] = 0b00111111;

	_spi->ChipSelect(true);
	_spi->Write(command.v[3]);
	_spi->Write(command.v[2]);
	_spi->Write(command.v[1]);
	_spi->Write(command.v[0]);
	_spi->ChipSelect(false);

	// CONFIG REGISTER
	command.Val = 0;
	command.v[3] = 0b01010100;
	command.v[2] = 0b00000000;
	command.v[1] = 0b00001111;
	command.v[0] = 0b11110001;

	_spi->ChipSelect(true);
	_spi->Write(command.v[3]);
	_spi->Write(command.v[2]);
	_spi->Write(command.v[1]);
	_spi->Write(command.v[0]);
	_spi->ChipSelect(false);
}

/*! \brief Default Destructor
 */
Mcp3903::~Mcp3903() { }

/*! \brief Sets the reference voltage for the ADC converstions.
 *
 * Sets the reference voltage for the ADC converstions.
 *
 * @param referenceVoltage Positive reference voltage
 */
void Mcp3903::SetReferenceVoltage(float referenceVoltage)
{
	this->_referenceVoltage = referenceVoltage;
}

/*! \brief Reads a single value from the Analog-to-Digital (ADC) converter
 *
 * Reads a single value from the Analog-to-Digital (ADC) converter\n
 *
 * @param channel Analog channel to read
 */
float Mcp3903::Read(UINT32 channel)
{
	UINT32_VAL command;
	unsigned char high, mid, low;
	int result;

	// Read Ch 1 (Ch0-)
	command.Val = 0;
	command.v[3] = 0b01000001 | ((channel << 1) & 0b00111110);
	_spi->ChipSelect(true);
	_spi->Write(command.v[3]);

	high = _spi->Read();
	mid = _spi->Read();
	low = _spi->Read();
	_spi->ChipSelect(false);

	result = 0;
	result |= (high << 24);
	result |= (mid << 16);
	result |= (low << 8);

	result /= 256;

	return (result * this->_referenceVoltage) / (3.0f * this->_quantizationLevels);
}